﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.Contracts;
using System.IO;
using System.Reflection;
using System.Text;

namespace OpenLS.Spreadsheet.Formats.Biff.Records
{
    struct IcvXF
    {
        private byte _value;
        public static implicit operator IcvXF(byte value)
        {
            Contract.Requires(value >= 0x00);
            Contract.Requires(value <= 0x3F || value == DefaultBackground || value == DefaultForeground || value == 0x48 || value == NoBorder);
            return new IcvXF() { _value = value };
        }
        public static explicit operator byte (IcvXF value)
        {
            return value._value;
        }
        public static bool operator ==(IcvXF value, IcvXF value2)
        {
            return value._value == value2._value;
        }

        public static bool operator !=(IcvXF value, IcvXF value2)
        {
            return !(value == value2);
        }

        public static byte NoBorder
        {
            get { return 0; }
        }

        public static byte BlackBorder
        {
            get { return 0x40; }
        }

        public static byte DefaultBackground
        {
            get { return 0x41; }
        }

        public static byte DefaultForeground
        {
            get { return 0x40; }
        }
    }
    struct CellXF
    {
#if DEBUG
        public override string ToString()
        {
            StringBuilder stringBuilder = new StringBuilder();
            stringBuilder.Append(GetType().Name);
            stringBuilder.Append("{");
            IEnumerable<FieldInfo> fields = GetType().GetFields(BindingFlags.FlattenHierarchy | BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.FlattenHierarchy | BindingFlags.GetField);
          //  var strings = Enumerable.Select(fields, GetValueOf);
          //  stringBuilder.Append(string.Join(", ", strings));
            stringBuilder.Append("}");
            return stringBuilder.ToString();
        }
        string GetValueOf  (FieldInfo f)
        {
            return f.Name.Replace(">k__BackingField", "") + " = " +
                   NiceValue(f.GetValue(this));
        }
        static string NiceValue(object o)
        {
            if (o == null)
                return "null";
            if (o is string)
                return o.ToString();
            if (o is IEnumerable)
            {
                List<string> ss = new List<string>();
                foreach (var oo in ((IEnumerable)o))
                    ss.Add(NiceValue(oo));
                return string.Format("{{{0}}}", string.Join(",", ss.ToArray()));
            }
            return o.ToString();
        }
#endif

        internal HorizontalCellAlignment alc;
        public bool fWrap;
        public VerticalCellAlignment alcV;
        private bool fJustLast;
        private byte trot;
        private byte cIndent;
        private bool fShrinkToFit;
        private byte _iReadOrder;
        public bool _fAtrNum;
        public bool fAtrFnt;
        public bool fAtrAlc;
        public bool fAtrBdr;
        public bool fAtrPat;
        public bool fAtrProt;
        public BorderStyle dgLeft;
        public BorderStyle dgRight;
        public BorderStyle dgTop;
        public BorderStyle dgBottom;
        public IcvXF icvLeft;
        public IcvXF icvRight;
        private byte grbitDiag;
        public IcvXF icvTop;
        public IcvXF icvBottom;
        public IcvXF icvDiag;
        public BorderStyle dgDiag;
        private bool _fHasXFExt;
        private byte _fls;
        public IcvXF IcvFore;
        public IcvXF icvBack;
        public bool fsxButton;

        public void Read (BinaryReader reader)
        {
            byte byte1 = reader.ReadByte();
            this.alc = (HorizontalCellAlignment)(byte1 & 0x7);
            this.fWrap = (byte1 & 0x08) != 0;
            this.alcV = (VerticalCellAlignment) ((byte1 >> 4) & 0x07);
            this.fJustLast = (byte1 & 0x80) != 0;
            Contract.Assert(Byte1 == byte1);
            this.trot = reader.ReadByte();
            byte byte3 = reader.ReadByte();
            this.cIndent = (byte) (byte3 & 0x0F);
            this.fShrinkToFit = (byte3 & 0x10) != 0;
            this._iReadOrder = (byte) ((byte3 >> 6) & 0x3);
            Contract.Assert(this.Byte3 == byte3);
            byte byte4 = reader.ReadByte();
            this._fAtrNum = (byte4 & 0x04) != 0;
            this.fAtrFnt = (byte4 & 0x08) != 0;
            this.fAtrAlc = (byte4 & 0x10) != 0;
                this.fAtrBdr = (byte4 & 0x20) != 0;
                    this.fAtrPat = (byte4 & 0x40) != 0;
            fAtrProt = (byte4 & 0x80) != 0;
            Contract.Assert(this.Byte4 == byte4);
            byte byte5 = reader.ReadByte();
            this.dgLeft = (BorderStyle)(byte5 & 0xF);
            this.dgRight = (BorderStyle)(byte5 >> 4);
            Contract.Assert((this.Byte5 == byte5));
            byte byte6 = reader.ReadByte();
            this.dgTop = (BorderStyle) (byte6 & 0xF);
            this.dgBottom = (BorderStyle) (byte6 >> 4);
            Contract.Assert((this.Byte6 == byte6));
            ushort ushort7 = reader.ReadUInt16();
            this.icvLeft = (byte) (ushort7 & 0x7F);
            this.icvRight = (byte) ((ushort7 >> 7) & 0x7F);
            this.grbitDiag = (byte) (ushort7 >> 14);
            Contract.Assert(this.UShort7 == ushort7);
            uint line3 = reader.ReadUInt32();
            icvTop = (byte) (line3 & 0x7F);
            icvBottom = (byte) ((line3 >> 7) & 0x7F);
            this.icvDiag = (byte) ((line3 >> 14) & 0x7F);
            this.dgDiag = (BorderStyle) ((line3 >> 21) & 0xF);
            this._fHasXFExt = (((line3 >> 25) & 0x1) != 0);
            _fls = (byte) (line3 >> 26);
            Contract.Assert(this.Line3 == line3);
            ushort line4 = reader.ReadUInt16();
            IcvFore = (byte) (line4 & 0x7F);
            icvBack = (byte) (line4 >> 7 & 0x7F);
            fsxButton = ((line4 >> 14) & 0x2) != 0;
            Contract.Assert(this.Line4 == line4);

        }

        public ushort Line4
        {
            get {
                return (ushort)((byte)IcvFore | (byte)icvBack << 7 | toByte(fsxButton) << 14);
            }
        }

        public uint Line3
        {
            get {
                return (uint)((byte)icvTop | (byte)icvBottom << 7 | (byte)icvDiag << 14 | (byte)dgDiag << 21 | toByte(_fHasXFExt) << 25 | _fls << 26);
            }
        }

        public ushort UShort7
        {
            get {
                return (ushort)((byte)icvLeft | (byte)icvRight << 7 | grbitDiag << 14);
            }
        }

        public byte Byte6
        {
            get { return (byte) ((byte)dgTop | (byte)dgBottom << 4); }
        }

        public byte Byte5
        {
            get
            {
                return (byte) ((byte)dgLeft | (byte)dgRight << 4);
            }
        }

        public byte Byte4
        {
            get
            {
                return (byte) (toByte(_fAtrNum) << 2 |
                               toByte(fAtrFnt) << 3 |
                               toByte(fAtrAlc) << 4 |
                               toByte(fAtrBdr) << 5 |
                               toByte(fAtrPat) << 6 |
                               toByte(fAtrProt) << 7);
            }
        }

        public byte Byte3
        {
            get {
                return (byte) (cIndent | toByte(fShrinkToFit) << 4 | _iReadOrder << 6);
            }
        }

        private byte toByte(bool b)
        {
            return (byte) (b ? 1 : 0);
        }

        public byte Byte1
        {
            get {
                int a = fWrap ? 1 : 0;
                int b = fJustLast ? 1 : 0;
                return (byte) ((byte)alc | a << 3 | (byte)alcV << 4 | b << 7);
            }
        }

        public BackgroundPattern _backgroundPattern
        {
            get { return (BackgroundPattern) _fls; }
            set { _fls = (byte) value; }
        }

        public void Write(BinaryWriter writer, bool hasXFExt)
        {
            this._fHasXFExt = hasXFExt;
            writer.Write(this.Byte1);
            writer.Write(this.trot);
            writer.Write(Byte3);
            writer.Write(Byte4);
            writer.Write(Byte5);
            writer.Write(Byte6);
            writer.Write(this.UShort7);
            writer.Write(Line3);
            writer.Write(Line4);
        }
    }
}